﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using ET.CommonLibrary.Log;
using ET.Score.SharedLibrary.TopicEvent;

namespace ET.Score.SharedLibrary.Wcf
{
    /// <summary>
    /// 通用心跳执行器实现
    /// </summary>
    /// <typeparam name="TService"></typeparam>
    public class HeartBeatInvoker<TService> : IDisposable
    {
        private readonly ChannelFactory<TService> factory;
        private readonly Func<TService, bool> heartBeatMethod;

        private WrappedTimer heartBeatTimer;

        /// <summary>
        /// 如果配置文件中没有指定心跳间隔，则使用此默认值
        /// </summary>
        private const int DefaultHeartBeatInteval = 4000;

        private TService proxy;

        //记录当前连接状态
        private bool isConnected;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="heartBeatMethod">心跳相关逻辑委托，如果心跳执行成功应返回true，否则返回false</param>
        public HeartBeatInvoker(ChannelFactory<TService> factory, Func<TService, bool> heartBeatMethod)
        {
            if (factory == null)
                throw new ArgumentNullException("factory");
            if (heartBeatMethod == null)
                throw new ArgumentNullException("heartBeatMethod");

            this.factory = factory;
            this.heartBeatMethod = heartBeatMethod;

            int interval = GetHeartBeatInterval();
            if (interval < 0)
            {
                EnvironmentContext.Logger
                    .Error("获取默认心跳间隔时间失败，请检查配置。将使用默认值:{0}(ms)", DefaultHeartBeatInteval);
            }

            heartBeatTimer = new WrappedTimer()
            {
                Interval = GetHeartBeatInterval(),
            };
            heartBeatTimer.Tick += heartBeatTimer_Tick;
        }

        public bool IsConnected
        {
            get { return isConnected; }
        }

        public void Start()
        {
            heartBeatTimer.Start();
            EnvironmentContext.Logger.Info("心跳执行器已启动");
        }

        public void Stop()
        {
            heartBeatTimer.Stop();
        }

        private int GetHeartBeatInterval()
        {
            string str = EnvironmentContext.AppSettings["HeartBeatInterval"];
            if (str == null)
            {
                return DefaultHeartBeatInteval;
            }

            int interval;

            if (int.TryParse(str, out interval))
            {
                return interval > 0 ? interval : DefaultHeartBeatInteval;
            }
            return DefaultHeartBeatInteval;
        }


        private void heartBeatTimer_Tick(object sender, EventArgs e)
        {
            try
            {
                if (proxy == null)
                    proxy = factory.CreateChannel();

                bool result = heartBeatMethod(proxy);
                if (result ^ isConnected)
                {
                    EnvironmentContext.Logger
                        .Warn(result ? "心跳服务:已重新连接到服务器" : "心跳服务:服务器连接断开.");
                    isConnected = result;
                }
            }
            catch (Exception ex)
            {
                HandleException(ex);
                if (isConnected)
                {
                    isConnected = false;
                }
                proxy = factory.CreateChannel();
            }
            OnConnectionStateChanged(isConnected);
        }

        private void HandleException(Exception ex)
        {
            if (ex is EndpointNotFoundException)
            {
                EnvironmentContext.Logger.Warn("心跳服务:服务器连接断开. 原因:找不到服务器,服务未开启或地址配置有误.");
                return;
            }
            if (ex is TimeoutException)
            {
                EnvironmentContext.Logger.Warn("心跳服务:服务器连接超时. 原因:{0}", ex.Message);
                return;
            }
            if (ex is CommunicationException)
            {
                EnvironmentContext.Logger.Warn("心跳服务:服务器连接断开. 原因:{0}", ex.Message);
                return;
            }
            EnvironmentContext.Logger.Error("心跳服务:服务器连接断开. \n异常信息:{0}", ex.ToString());
        }


        protected virtual void OnConnectionStateChanged(bool connected)
        {
            EnvironmentContext.TopicEventAggregator
                .GetTopicEvent<ConnectionStateChangedEvent>()
                .Publish(connected);
        }

        #region IDisposable 成员

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
            }
            this.heartBeatTimer.Dispose();
            var disposable = proxy as IDisposable;
            if (disposable != null)
                disposable.Dispose();
        }

        #endregion
    }
}
